package com.xrui.hbase;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Multimap;
import com.xrui.hbase.schema.spec.ColumnReaderSpec;


import java.io.IOException;
import java.util.Map;

/**
 * Builder for {@link TableReader}s with options.
 * <p>
 * <p>Options include:</p>
 * <ul>
 * <li>
 * Setting the behavior of a reader when a cell decoder cannot be found in the decoder cache.
 * </li>
 * <li>Setting a map of overriding default read behavior per column.</li>
 * <li>
 * Setting a map of alternative read behaviors which will be included in the decoder cache, but
 * which will not change default read behavior.
 * </li>
 * </ul>
 */
public interface TableReaderBuilder {

    /**
     * By default, build and cache new cell decoders for unknown
     * {@link ColumnReaderSpec} overrides.
     */
    OnDecoderCacheMiss DEFAULT_CACHE_MISS = OnDecoderCacheMiss.BUILD_AND_CACHE;

    /**
     * By default, do not override any column read behaviors.
     */
    Map<ColumnName, ColumnReaderSpec> DEFAULT_READER_SPEC_OVERRIDES = ImmutableMap.of();

    /**
     * By default, do not include any alternate column reader specs.
     */
    Multimap<ColumnName, ColumnReaderSpec> DEFAULT_READER_SPEC_ALTERNATIVES =
        ImmutableSetMultimap.of();

    /**
     * Configure the KijiTableReaderOptions to include the given OnDecoderCacheMiss behavior. If unset
     * this option defaults to
     * {@link TableReaderBuilder.OnDecoderCacheMiss#BUILD_AND_CACHE}.
     *
     * @param behavior OnDecoderCacheMiss behavior to use when a {@link ColumnReaderSpec} override
     *                 specified in a {@link DataRequest} cannot be found in the prebuilt cache of cell
     *                 decoders.
     * @return this builder.
     */
    TableReaderBuilder withOnDecoderCacheMiss(OnDecoderCacheMiss behavior);

    /**
     * Get the configured OnDecoderCacheMiss behavior or null if none has been set.
     *
     * @return the configured OnDecoderCacheMiss behavior or null if none has been set.
     */
    OnDecoderCacheMiss getOnDecoderCacheMiss();

    /**
     * Configure the KijiTableReaderOptions to include the given ColumnReaderSpec overrides. These
     * ColumnReaderSpecs will be used to determine read behavior for associated columns. These
     * overrides will change the default behavior of the associated column when read by this
     * reader, even when no ColumnReaderSpec is specified in a DataRequest. If unset this option
     * defaults to not override any columns.
     *
     * @param overrides mapping from columns to overriding read behavior for those columns.
     * @return this builder.
     */
    TableReaderBuilder withColumnReaderSpecOverrides(
            Map<ColumnName, ColumnReaderSpec> overrides);

    /**
     * Get the configured ColumnReaderSpec overrides or null if none have been set.
     *
     * @return the configured ColumnReaderSpec overrides or null if none have been set.
     */
    Map<ColumnName, ColumnReaderSpec> getColumnReaderSpecOverrides();

    /**
     * Configure the KijiTableReaderOptions to include the given ColumnReaderSpecs as alternate
     * reader schema options for the associated columns. Setting these alternatives does not
     * change the behavior of associated columns when ColumnReaderSpecs are not included in
     * KijiDataRequests. ColumnReaderSpecs included here can be used as reader spec overrides in
     * KijiDataRequests without triggering {@link OnDecoderCacheMiss#FAIL} and without the cost
     * associated with constructing a new cell decoder. If unset this option defaults to not create
     * any alternative cell decoders.
     * <p>
     * <p>
     * Note: ColumnReaderSpec overrides provided to
     * {@link #withColumnReaderSpecOverrides(Map)} should not be duplicated here.
     * </p>
     *
     * @param alternatives mapping from columns to reader spec alternatives which the
     *                     TableReader will accept as overrides in data requests.
     * @return this builder.
     */
    TableReaderBuilder withColumnReaderSpecAlternatives(
            Multimap<ColumnName, ColumnReaderSpec> alternatives);

    /**
     * Get the configured ColumnReaderSpec alternatives or null if none have been set.
     *
     * @return the configured ColumnReaderSpec alternatives or null if none have been set.
     */
    Multimap<ColumnName, ColumnReaderSpec> getColumnReaderSpecAlternatives();

    /**
     * Build a new KijiTableReaderOptions from the values set in this builder. The user is responsible
     * for closing this reader when it is no longer needed.
     *
     * @return a new KijiTableReaderOptions from the values set in this builder.
     * @throws IOException in case of an error building the reader.
     */
    TableReader buildAndOpen() throws IOException;

    /**
     * Optional behavior when a {@link ColumnReaderSpec} override specified in a
     * {@link DataRequest} used with this reader is not found in the prebuilt cache of cell
     * decoders. Default is BUILD_AND_CACHE.
     */
    enum OnDecoderCacheMiss {
        /**
         * Throw an exception to indicate that the override is not supported.
         */
        FAIL,
        /**
         * Build a new cell decoder based on the override and store it to the cache.
         */
        BUILD_AND_CACHE,
        /**
         * Build a new cell decoder based on the override, but do not store it to the cache.
         */
        BUILD_DO_NOT_CACHE
    }
}
